
class Hud:
    def __init__(self, pos, width = 640, height = 60):
        self.pos = pos
        self.width = width
        self.height = height
        self.fps = "111"
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        pygame.draw.line(self.background, (12, 255, 0), (0, 0),
            (self.width, 0))
        text = self.font.render("FPS:", False, (12, 255, 0))
        self.background.blit(text, (0, 0))

        text = self.font.render("Lives:", False, (12, 255, 0))
        self.background.blit(text, (0, 30))

        text = self.font.render("Weapon:", False, (12, 255, 0))
        self.background.blit(text, (450, 0))

        text = self.font.render("Ammo:", False, (12, 255, 0))
        self.background.blit(text, (450, 30))

        self.fps_pos = (pos[0] + 50, pos[1])
        self.lives_pos = (pos[0] + 80, pos[1] + 30)
        self.weapon_pos = (560, pos[1])
        self.ammo_pos = (560, pos[1] + 30)
    def Render(self, surface, engine):
        surface.blit(self.background, self.pos)
        text = self.font.render(self.fps, False, (12, 255, 0))
        surface.blit(text, self.fps_pos)
        text = self.font.render(engine.g.weapon.name, False, (12, 255, 0))
        surface.blit(text, self.weapon_pos)
        text = self.font.render(str(engine.lives), False, (12, 255, 0))
        surface.blit(text, self.lives_pos)
        if engine.g.weapon.ammo == -1:
            txt = "Infinite"
        else:
            txt = str(engine.g.weapon.ammo)
        text = self.font.render(txt, False, (12, 255, 0))
        surface.blit(text, self.ammo_pos)

class MenuContext:
    def __init__(self, screen, game, pos, width = 640, height=480):
        self.game = game
        self.screen = screen
        self.pos = pos
        self.width = width
        self.height = height
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        text = self.font.render("New Game", False, (12, 255, 0))
        self.background.blit(text, (250, 100))
        text = self.font.render("Continue", False, (12, 255, 0))
        self.background.blit(text, (250, 140))
        text = self.font.render("Instructions", False, (12, 255, 0))
        self.background.blit(text, (250, 180))
        text = self.font.render("Quit", False, (12, 255, 0))
        self.background.blit(text, (285, 220))
        self.event_handlers = {}
        self.mouse_handlers = {
            BUTTON_LEFT: self.button_pressed,
        }
    def button_pressed(self, event):
        x, y = event.pos
        if y > 100 and y < 140:
            self.game.new_game()
        if y > 140 and y < 180:
            self.game.continue_game()
        if y > 180 and y < 220:
            self.game.context = self.game.instruction_context
            self.game.event_handlers = self.game.instruction_context.event_handlers
            self.game.mouse_handlers = self.game.instruction_context.mouse_handlers
        if y > 220 and y < 260:
            sys.exit(0)
    def Render(self):
        self.screen.blit(self.background, (0, 0))
    def Update(self, delta):
        pass

class InstructionsContext:
    def __init__(self, screen, game, width=640, height=480):
        self.game = game
        self.screen = screen
        self.pos = (0, 0)
        self.width = width
        self.height = height
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        text = self.font.render("Welcome to the G Underground.", False, (12, 255, 0))
        self.background.blit(text, (100, 100))
        text = self.font.render("The Hero G is Trapped Underground in these nasty caves.", False, (12, 255, 0))
        self.background.blit(text, (10, 140))
        text = self.font.render("Help him blast his way out while fighting off zombies.", False, (12, 255, 0))
        self.background.blit(text, (10, 170))
        text = self.font.render("Move (the black dot) around with wasd keys", False, (12, 255, 0))
        self.background.blit(text, (10, 200))
        text = self.font.render("Shoot by tapping the left mouse button", False, (12, 255, 0))
        self.background.blit(text, (10, 230))
        text = self.font.render("Change weapons by clicking the right mouse button", False, (12, 255, 0))
        self.background.blit(text, (10, 260))
        text = self.font.render("Get to the exit portal (green dot) to escape the level", False, (12, 255, 0))
        self.background.blit(text, (10, 290))
        text = self.font.render("Only rockets can destroy the walls and you only have 10!!", False, (12, 255, 0))
        self.background.blit(text, (10, 320))
        text = self.font.render("Missiles can go around corners.", False, (12, 255, 0))
        self.background.blit(text, (10, 350))

        self.event_handlers = {}
        self.mouse_handlers = {
            BUTTON_LEFT: self.button_pressed
        }
    def button_pressed(self, event):
        self.game.context = self.game.menu_context
        self.game.event_handlers = self.game.menu_context.event_handlers
        self.game.mouse_handlers = self.game.menu_context.mouse_handlers
    def Render(self):
        self.screen.blit(self.background, (0, 0))
    def Update(self, delta):
        pass
        
    
class DeathContext:
    def __init__(self, screen, game, width=640, height=480):
        self.game = game
        self.screen = screen
        self.pos = (0, 0)
        self.width = width
        self.height = height
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        text = self.font.render("You have been Killed!", False, (12, 255, 0))
        self.background.blit(text, (200, 100))
        text = self.font.render("Click on the screen to continue", False, (12, 255, 0))
        self.background.blit(text, (150, 140))
        self.event_handlers = {}
        self.mouse_handlers = {
            BUTTON_LEFT: self.button_pressed,
        }
    def button_pressed(self, event):
        self.game.level_context.lives -= 1
        self.game.level_context.enemies = []
        self.game.level_context.g.weapons[1].ammo = 10
        self.game.level_context.g.weapons[2].ammo = 2 
        self.game.level_context.g.dir.x = 0
        self.game.level_context.g.dir.y = 0
        self.game.level_context.g.pos = Vector(50, 50)
        self.game.continue_game()
    def Render(self):
        self.screen.blit(self.background, (0, 0))
    def Update(self, delta):
        pass

class GameEnd:
    def __init__(self, screen, game, width=640, height=480):
        self.game = game
        self.screen = screen
        self.width = width
        self.height = height
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        text = self.font.render("You have finished the game! Congratulations.", False, (12, 255, 0))
        self.background.blit(text, (100, 100))
        self.event_handlers = {
            K_ESCAPE: self.display_menu,
        }
        self.mouse_handlers = {
            BUTTON_LEFT: self.display_menu,
        }
    def display_menu(self, event):
        self.game.context = self.game.menu_context
        self.game.event_handlers = self.game.menu_context.event_handlers
        self.game.mouse_handlers = self.game.menu_context.mouse_handlers
    def Render(self):
        self.screen.blit(self.background, (0, 0))
    def Update(self, delta):
        pass

class NextLevelScreen:
    def __init__(self, screen, game, width=640, height=480):
        self.game = game
        self.screen =screen
        self.width = width
        self.height = height
        self.font = pygame.font.SysFont("Arial", 32)
        background = pygame.Surface((width, height))
        self.background = background.convert()
        text = self.font.render("You are up to the next level.", False, (12, 255, 0))
        self.background.blit(text, (180, 100))
        self.event_handlers = {
            K_ESCAPE: self.display_level,
        }
        self.mouse_handlers = {
            BUTTON_LEFT: self.display_level,
        }

    def display_level(self, event):
        self.game.level_context.setup_level()
        self.game.context = self.game.level_context
        self.game.mouse_handlers = self.game.level_context.mouse_handlers
        self.game.event_handlers = self.game.level_context.event_handlers
    def Render(self):
        self.screen.blit(self.background, (0, 0))
    def Update(self, delta):
        pass
        
class GameContext:
    def __init__(self, screen, game, width = 640, height = 480):
        self.game = game
        self.screen = screen
        self.arena_map = ArenaMap(self.screen.get_size())
        self.hud = Hud((0, 480))

        self.levels = [
            MapData("level1.png", [(600, 40), (600, 400)], (200, 200)),
            MapData("level2.png", [(600, 40), (600, 400)], (200, 200)),
            MapData("level3.png", [(600, 40), (200, 400)], (300, 400)),
            MapData("level4.png", [(600, 60), (200, 400)], (600, 400)),
            MapData("level5.png", [(200, 400), (250, 400)], (600, 400)),
            MapData("level6.png", [(200, 200), (500, 200)], (600, 400)),
        ]
        self.level_index = 0
        self.lives = 5
        self.setup_level()

    def setup_level(self):
        self.arena_map.LoadMap(self.levels[self.level_index])
        self.level_index += 1
        self.g = ControlableEntity(50, 50)
        self.particles = []
        self.enemies = []
        self.event_handlers = {
            K_d: self.g.move_right,
            K_a: self.g.move_left,
            K_w: self.g.move_up,
            K_s: self.g.move_down,
            K_ESCAPE: self.to_menu
        }
        self.mouse_handlers = {
            BUTTON_LEFT: self.FireWeapon,
            BUTTON_RIGHT: self.ChangeWeapon,
        }
    def to_menu(self, event):
        self.game.event_handlers = self.game.menu_context.event_handlers
        self.game.mouse_handlers = self.game.menu_context.mouse_handlers
        self.game.context = self.game.menu_context
    def die(self):
        if self.lives == 0:
            self.game.level_context = None
            self.to_menu(None)
            return
        self.game.context = self.game.death_context
        self.game.event_handlers = self.game.death_context.event_handlers
        self.game.mouse_handlers = self.game.death_context.mouse_handlers
        
    def Update(self, delta):
        self.g.Update(delta, self)
        if self.g.pos.is_close(Vector(*self.arena_map.exit.pos)):
            if self.level_index == len(self.levels):
                self.game.level_context = None
                self.game.context = self.game.game_end
                self.game.event_handlers = self.game.game_end.event_handlers
                self.game.mouse_handlers = self.game.game_end.mouse_handlers
            else:
                self.game.context = self.game.next_level
                self.game.event_handlers = self.game.next_level.event_handlers
                self.game.mouse_handlers = self.game.next_level.mouse_handlers
        for particle in self.particles:
            particle.Update(delta, self)
            for enemy in self.enemies:
                if particle.pos.is_close(enemy.pos, 8.01):
                    if particle in self.particles:
                        self.particles.remove(particle)
                    self.enemies.remove(enemy)
        for spawner in self.arena_map.spawns:
            spawner.Update(delta, self)
        for enemy in self.enemies:
            enemy.Update(delta, self)
            if enemy.pos.is_close(self.g.pos):
                self.die()
    def Render(self):
        self.arena_map.Render(self.screen)
        self.g.Render(self.screen)
        for particle in self.particles:
            particle.Render(self.screen)
        for spawner in self.arena_map.spawns:
            spawner.Render(self.screen)
        for enemy in self.enemies:
            enemy.Render(self.screen)
        self.hud.Render(self.screen, self)
        
        pygame.display.flip()
    def FireWeapon(self, event):
        particle = self.g.Fire(event, self)
        if not particle:
            return
        """
        dir = Vector(*event.pos) - self.g.pos
        particle = Particle(self.g.pos)
        dir.normalise(particle.vel)
        particle.dir = dir
        """
        self.particles.append(particle)
    def ChangeWeapon(self, event):
        self.g.toggle_weapon()
        
    def spawn_enemy(self, pos):
        self.enemies.append(Troll(pos, self))

class GArena:
    def __init__(self, width = 640, height = 540):
        pygame.init()
        self.width = width
        self.height = height
        pygame.display.set_caption('G Underground')
        self.screen = pygame.display.set_mode((self.width, self.height))
        self.level_context = None
        self.game_end = GameEnd(self.screen, self)
        self.next_level = NextLevelScreen(self.screen, self)
        self.death_context = DeathContext(self.screen, self)
        self.menu_context = MenuContext(self.screen, self, (0, 0))
        self.instruction_context = InstructionsContext(self.screen, self)
        self.event_handlers = self.menu_context.event_handlers
        self.mouse_handlers = self.menu_context.mouse_handlers
        self.context = self.menu_context

    def new_game(self):
        self.level_context = GameContext(self.screen, self)
        self.context = self.level_context
        self.event_handlers = self.level_context.event_handlers
        self.mouse_handlers = self.level_context.mouse_handlers

    def continue_game(self):
        if not self.level_context:
            return
        self.context = self.level_context
        self.event_handlers = self.level_context.event_handlers
        self.mouse_handlers = self.level_context.mouse_handlers

    def Update(self, delta):
        pass

    def Render(self):
        self.context.Render()
        pygame.display.flip()

    def MainLoop(self):
        clock = pygame.time.Clock()
        while 1:
            delta = clock.tick()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
                elif event.type == KEYDOWN or event.type == KEYUP:
                    handler = self.event_handlers.get(event.key)
                    if handler:
                        handler(event)
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    handler = self.mouse_handlers.get(event.button)
                    if handler:
                        handler(event)
                    pass
                elif event.type == pygame.ACTIVEEVENT and self.level_context:
                    self.level_context.g.dir.x = 0
                    self.level_context.g.dir.y = 0
                    
            #self.Update(delta)
            self.context.Update(delta)
            if self.level_context:
                self.level_context.hud.fps = str(round(clock.get_fps()))
            self.Render()

